/*
    KMC Simulation
    Copyright (c) 2012 Filip Rozbořil

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "ClusterCollection.h"
#include "Grid.h"

ClusterCollection :: ClusterCollection(Grid& g)
{
 	int32_t currentCluster = 0;
 	vector<GridAtom*> neighbors;
 	
 	for(uint32_t y = 0; y < g.getRows(); y++)
 	{
 	 	for(uint32_t x = 0; x < g.getCols(); x++)
 	 	{
 	 		GridObject* obj = g.getObject(GridField(x, y));
 	 		if(obj->getType() == Object::AlAtom)
			{
				GridAtom* atom = (GridAtom*)obj;
				GridObject* nbro;
				nbro = g.getObject(g.getTop(GridField(x, y)));
			 	if(nbro != &GridObject::None)
			 	{
			 	 	GridAtom* nbra = (GridAtom*)nbro;
			 	 	if(getAtomCluster(nbra) >= 0)
			 	 	{
			 	 	 	neighbors.push_back(nbra);
			 	 	}
			 	}
			 	nbro = g.getObject(g.getBottom(GridField(x, y)));
			 	if(nbro != &GridObject::None)
			 	{
			 	 	GridAtom* nbra = (GridAtom*)nbro;
			 	 	if(getAtomCluster(nbra) >= 0)
			 	 	{
			 	 	 	neighbors.push_back(nbra);
			 	 	}
			 	}
			 	if((y % 2) == 0) //even row
		 		{
		 			nbro = g.getObject(g.getBottomRight(GridField(x, y)));
		 		}
		 		else
		 		{
		 		 	nbro = g.getObject(g.getTopRight(GridField(x, y)));
		 		}
			 	if(nbro != &GridObject::None)
			 	{
			 	 	GridAtom* nbra = (GridAtom*)nbro;
			 	 	if(getAtomCluster(nbra) >= 0)
			 	 	{
			 	 	 	neighbors.push_back(nbra);
			 	 	}
			 	}
			 	if((y % 2) == 0) //even row
		 		{
		 			nbro = g.getObject(g.getBottomLeft(GridField(x, y)));
		 		}
		 		else
		 		{
		 		 	nbro = g.getObject(g.getTopLeft(GridField(x, y)));
		 		}
			 	if(nbro != &GridObject::None)
			 	{
			 	 	GridAtom* nbra = (GridAtom*)nbro;
			 	 	if(getAtomCluster(nbra) >= 0)
			 	 	{
			 	 	 	neighbors.push_back(nbra);
			 	 	}
			 	}
			 	assignCluster(atom, currentCluster, neighbors);
			 	neighbors.clear();
			}
 	 	} 	
 	}

}

void ClusterCollection :: assignCluster(GridAtom* atom, int32_t& currentCluster, vector<GridAtom*>& neighbors)
{
	if(neighbors.size() == 1)
 	{
 	 	GridAtom* neighbor = neighbors[0];
 	 	int32_t neighborCluster = getAtomCluster(neighbor);
 	 	mClusters[neighborCluster]->push_back(atom);
 	 	mAtomClusters[atom] = neighborCluster;
 	}
 	else if(neighbors.size() == 0)
 	{
 		mClusters.push_back(new vector<GridAtom*>());
		mClusters[currentCluster]->push_back(atom);
		mAtomClusters[atom] = currentCluster;
 		currentCluster++; 
 	}
 	else //more than 1 neighbor
 	{
 		int32_t minCluster = mAtomClusters.size();
 		//find the least-numbered cluster among the neighbors
 		for(vector<GridAtom*>::size_type i = 0; i < neighbors.size(); i++)
 		{
 			int32_t atomCluster = getAtomCluster(neighbors[i]);
 		 	if(atomCluster < minCluster)
 		 	{
 		 	 	minCluster = atomCluster;
 		 	}
 		}
 		for(vector<GridAtom*>::size_type i = 0; i < neighbors.size(); i++)
 		{
 			int32_t atomCluster = getAtomCluster(neighbors[i]);
 		 	if(atomCluster != minCluster)
 		 	{
 		 		//merge clusters
 		 	 	mClusters[minCluster]->insert(mClusters[minCluster]->end(), mClusters[atomCluster]->begin(), mClusters[atomCluster]->end());
 		 	 	for(vector<GridAtom*>::iterator j = mClusters[atomCluster]->begin(); j != mClusters[atomCluster]->end(); j++)
 				{
 					mAtomClusters[*j] = minCluster;
 				}
 				mClusters[atomCluster]->clear();
 		 	}
 		}
 		//assign this atom to the cluster
 		mClusters[minCluster]->push_back(atom);
 	 	mAtomClusters[atom] = minCluster;
 	}
}

vector<uint32_t> ClusterCollection :: getClusters()
{
	vector<uint32_t> counts;
	for(auto i : mClusters)
 	{
 	 	uint32_t size = i->size();
 	 	if(size > 0)
 	 	{
 	 	 	while(counts.size() < size)
 	 	 	{
 	 	 		counts.push_back(0);
 	 	 	}
 	 	 	counts[size - 1]++;
 	 	}
	}
	
	return counts;
}
